-- BBE.physics - модульная физическая система для BlazeBolt game engine

-- Конфигурация по умолчанию
Physics_defaultConfig = {
    gravity = {0, -9.81, 0},
    worldBounds = {
        min = {-1000, -1000, -1000},
        max = {1000, 1000, 1000}
    },
    collisionLayers = 0xFFFF,
    fixedTimestep = 1/60,
    velocityIterations = 8,
    positionIterations = 3
}

-- Система слоев коллизий
Physics_collisionMatrix = {}
Physics_layers = {
    DEFAULT = 1,
    PLAYER = 2,
    ENEMY = 4,
    PROJECTILE = 8,
    TERRAIN = 16,
    TRIGGER = 32,
    SENSOR = 64
}

-- Матрица коллизий (какие слои сталкиваются с какими)
for i = 1, 64 do
    Physics_collisionMatrix[i] = 0xFFFF -- по умолчанию все сталкиваются со всеми
end

-- Вспомогательные математические функции
function vector3Add(a, b)
    return {a[1] + b[1], a[2] + b[2], a[3] + b[3]}
end

function vector3Sub(a, b)
    return {a[1] - b[1], a[2] - b[2], a[3] - b[3]}
end

function vector3Mul(v, s)
    return {v[1] * s, v[2] * s, v[3] * s}
end

function vector3Dot(a, b)
    return a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
end

function vector3Cross(a, b)
    return {
        a[2] * b[3] - a[3] * b[2],
        a[3] * b[1] - a[1] * b[3],
        a[1] * b[2] - a[2] * b[1]
    }
end

function vector3Length(v)
    return math.sqrt(v[1] * v[1] + v[2] * v[2] + v[3] * v[3])
end

function vector3Normalize(v)
    local len = vector3Length(v)
    if len > 0 then
        return {v[1] / len, v[2] / len, v[3] / len}
    end
    return {0, 0, 0}
end

function vector2Dot(a, b)
    return a[1] * b[1] + a[2] * b[2]
end

function vector2Length(v)
    return math.sqrt(v[1] * v[1] + v[2] * v[2])
end

function vector2Normalize(v)
    local len = vector2Length(v)
    if len > 0 then
        return {v[1] / len, v[2] / len}
    end
    return {0, 0}
end

-- Создание информации о коллизии
function Physics_CreateCollisionInfo()
    local info = {}
    info.hasCollision = false
    info.normal = {0, 0, 0}
    info.penetration = 0
    info.point = {0, 0, 0}
    info.bodyA = nil
    info.bodyB = nil
    info.colliderA = nil
    info.colliderB = nil
    return info
end

-- Создание физического тела
function Physics_CreateBody(config)
    local body = {}
    
    body.type = config.type or "dynamic"
    body.position = config.position or {0, 0, 0}
    body.velocity = config.velocity or {0, 0, 0}
    body.acceleration = config.acceleration or {0, 0, 0}
    body.rotation = config.rotation or {0, 0, 0}
    body.angularVelocity = config.angularVelocity or {0, 0, 0}
    body.mass = config.mass or 1.0
    body.invMass = body.mass > 0 and 1.0 / body.mass or 0
    body.restitution = config.restitution or 0.3
    body.friction = config.friction or 0.5
    body.damping = config.damping or 0.99
    body.angularDamping = config.angularDamping or 0.95
    body.collisionLayer = config.collisionLayer or Physics_layers.DEFAULT
    body.collisionMask = config.collisionMask or 0xFFFF
    body.isTrigger = config.isTrigger or false
    body.isSensor = config.isSensor or false
    body.enabled = config.enabled ~= false
    body.userData = config.userData or {}
    body.onCollision = config.onCollision
    body.onTrigger = config.onTrigger
    
    body.colliders = config.colliders or {}
    body.aabb = {min = {0, 0, 0}, max = {0, 0, 0}}
    body.broadphaseId = nil
    
    Physics_UpdateBodyAABB(body)
    
    return body
end

function Physics_UpdateBodyAABB(body)
    if #body.colliders == 0 then
        body.aabb.min = vector3Add(body.position, {-0.5, -0.5, -0.5})
        body.aabb.max = vector3Add(body.position, {0.5, 0.5, 0.5})
        return
    end
    
    local min = {math.huge, math.huge, math.huge}
    local max = {-math.huge, -math.huge, -math.huge}
    
    for _, collider in ipairs(body.colliders) do
        if collider.enabled then
            local colliderMin, colliderMax = Physics_GetColliderAABB(collider, body.position)
            for i = 1, 3 do
                min[i] = math.min(min[i], colliderMin[i])
                max[i] = math.max(max[i], colliderMax[i])
            end
        end
    end
    
    body.aabb.min = min
    body.aabb.max = max
end

function Physics_ApplyForce(body, force, point)
    point = point or body.position
    body.acceleration[1] = body.acceleration[1] + force[1] * body.invMass
    body.acceleration[2] = body.acceleration[2] + force[2] * body.invMass
    body.acceleration[3] = body.acceleration[3] + force[3] * body.invMass
end

function Physics_ApplyImpulse(body, impulse, point)
    point = point or body.position
    body.velocity[1] = body.velocity[1] + impulse[1] * body.invMass
    body.velocity[2] = body.velocity[2] + impulse[2] * body.invMass
    body.velocity[3] = body.velocity[3] + impulse[3] * body.invMass
end

function Physics_UpdateBody(body, dt)
    if not body.enabled then return end
    
    body.velocity[1] = body.velocity[1] + body.acceleration[1] * dt
    body.velocity[2] = body.velocity[2] + body.acceleration[2] * dt
    body.velocity[3] = body.velocity[3] + body.acceleration[3] * dt
    
    body.velocity[1] = body.velocity[1] * body.damping
    body.velocity[2] = body.velocity[2] * body.damping
    body.velocity[3] = body.velocity[3] * body.damping
    
    body.position[1] = body.position[1] + body.velocity[1] * dt
    body.position[2] = body.position[2] + body.velocity[2] * dt
    body.position[3] = body.position[3] + body.velocity[3] * dt
    
    body.acceleration = {0, 0, 0}
    
    Physics_UpdateBodyAABB(body)
end

-- Система коллайдеров
function Physics_CreateCollider(type, params)
    local collider = {}
    collider.type = type
    collider.offset = params.offset or {0, 0, 0}
    collider.enabled = params.enabled ~= false
    
    if type == "box" then
        collider.size = params.size or {1, 1, 1}
    elseif type == "sphere" then
        collider.radius = params.radius or 1
    elseif type == "capsule" then
        collider.radius = params.radius or 0.5
        collider.height = params.height or 2
    elseif type == "plane" then
        collider.normal = params.normal or {0, 1, 0}
        collider.distance = params.distance or 0
    end
    
    return collider
end

function Physics_GetColliderAABB(collider, bodyPosition)
    local worldPos = vector3Add(bodyPosition, collider.offset)
    
    if collider.type == "box" then
        local halfSize = vector3Mul(collider.size, 0.5)
        return {
            vector3Sub(worldPos, halfSize),
            vector3Add(worldPos, halfSize)
        }
    elseif collider.type == "sphere" then
        return {
            vector3Sub(worldPos, {collider.radius, collider.radius, collider.radius}),
            vector3Add(worldPos, {collider.radius, collider.radius, collider.radius})
        }
    elseif collider.type == "capsule" then
        return {
            vector3Sub(worldPos, {collider.radius, collider.height * 0.5, collider.radius}),
            vector3Add(worldPos, {collider.radius, collider.height * 0.5, collider.radius})
        }
    elseif collider.type == "plane" then
        return {
            {-math.huge, -math.huge, -math.huge},
            {math.huge, math.huge, math.huge}
        }
    end
    
    return {worldPos, worldPos}
end

-- Функции детектирования коллизий
function Collision_BoxVsBox(bodyA, colliderA, bodyB, colliderB)
    local posA = vector3Add(bodyA.position, colliderA.offset)
    local posB = vector3Add(bodyB.position, colliderB.offset)
    
    local halfSizeA = vector3Mul(colliderA.size, 0.5)
    local halfSizeB = vector3Mul(colliderB.size, 0.5)
    
    for i = 1, 3 do
        if math.abs(posA[i] - posB[i]) > halfSizeA[i] + halfSizeB[i] then
            return nil
        end
    end
    
    local info = Physics_CreateCollisionInfo()
    info.hasCollision = true
    info.bodyA = bodyA
    info.bodyB = bodyB
    info.colliderA = colliderA
    info.colliderB = colliderB
    
    local minPenetration = math.huge
    local normalAxis = 1
    
    for i = 1, 3 do
        local penetration = (halfSizeA[i] + halfSizeB[i]) - math.abs(posA[i] - posB[i])
        if penetration < minPenetration then
            minPenetration = penetration
            normalAxis = i
        end
    end
    
    info.penetration = minPenetration
    info.normal = {0, 0, 0}
    if posA[normalAxis] < posB[normalAxis] then
        info.normal[normalAxis] = 1
    else
        info.normal[normalAxis] = -1
    end
    
    info.point = {
        (posA[1] + posB[1]) * 0.5,
        (posA[2] + posB[2]) * 0.5,
        (posA[3] + posB[3]) * 0.5
    }
    
    return info
end

function Collision_SphereVsSphere(bodyA, colliderA, bodyB, colliderB)
    local posA = vector3Add(bodyA.position, colliderA.offset)
    local posB = vector3Add(bodyB.position, colliderB.offset)
    
    local delta = vector3Sub(posA, posB)
    local distance = vector3Length(delta)
    local minDistance = colliderA.radius + colliderB.radius
    
    if distance >= minDistance or distance == 0 then
        return nil
    end
    
    local info = Physics_CreateCollisionInfo()
    info.hasCollision = true
    info.bodyA = bodyA
    info.bodyB = bodyB
    info.colliderA = colliderA
    info.colliderB = colliderB
    
    info.normal = vector3Normalize(delta)
    info.penetration = minDistance - distance
    info.point = vector3Add(posB, vector3Mul(info.normal, colliderB.radius))
    
    return info
end

function Collision_BoxVsSphere(bodyA, colliderA, bodyB, colliderB)
    local boxPos = vector3Add(bodyA.position, colliderA.offset)
    local spherePos = vector3Add(bodyB.position, colliderB.offset)
    
    local halfSize = vector3Mul(colliderA.size, 0.5)
    
    local closestPoint = {
        math.max(boxPos[1] - halfSize[1], math.min(spherePos[1], boxPos[1] + halfSize[1])),
        math.max(boxPos[2] - halfSize[2], math.min(spherePos[2], boxPos[2] + halfSize[2])),
        math.max(boxPos[3] - halfSize[3], math.min(spherePos[3], boxPos[3] + halfSize[3]))
    }
    
    local delta = vector3Sub(spherePos, closestPoint)
    local distance = vector3Length(delta)
    
    if distance >= colliderB.radius then
        return nil
    end
    
    local info = Physics_CreateCollisionInfo()
    info.hasCollision = true
    info.bodyA = bodyA
    info.bodyB = bodyB
    info.colliderA = colliderA
    info.colliderB = colliderB
    
    if distance > 0 then
        info.normal = vector3Normalize(delta)
        info.penetration = colliderB.radius - distance
        info.point = closestPoint
    else
        local distToMin = {
            spherePos[1] - (boxPos[1] - halfSize[1]),
            spherePos[2] - (boxPos[2] - halfSize[2]),
            spherePos[3] - (boxPos[3] - halfSize[3])
        }
        local distToMax = {
            (boxPos[1] + halfSize[1]) - spherePos[1],
            (boxPos[2] + halfSize[2]) - spherePos[2],
            (boxPos[3] + halfSize[3]) - spherePos[3]
        }
        
        local minDist = math.huge
        local normalAxis = 1
        for i = 1, 3 do
            local dist = math.min(distToMin[i], distToMax[i])
            if dist < minDist then
                minDist = dist
                normalAxis = i
            end
        end
        
        info.normal = {0, 0, 0}
        if distToMin[normalAxis] < distToMax[normalAxis] then
            info.normal[normalAxis] = -1
        else
            info.normal[normalAxis] = 1
        end
        
        info.penetration = colliderB.radius + minDist
        info.point = vector3Sub(spherePos, vector3Mul(info.normal, colliderB.radius))
    end
    
    return info
end

function Collision_Check(bodyA, colliderA, bodyB, colliderB)
    local typeA, typeB = colliderA.type, colliderB.type
    
    if typeA == "box" and typeB == "box" then
        return Collision_BoxVsBox(bodyA, colliderA, bodyB, colliderB)
    elseif typeA == "sphere" and typeB == "sphere" then
        return Collision_SphereVsSphere(bodyA, colliderA, bodyB, colliderB)
    elseif (typeA == "box" and typeB == "sphere") then
        return Collision_BoxVsSphere(bodyA, colliderA, bodyB, colliderB)
    elseif (typeA == "sphere" and typeB == "box") then
        return Collision_BoxVsSphere(bodyB, colliderB, bodyA, colliderA)
    end
    
    return nil
end

-- Система физического мира
function Physics_CreateWorld(config)
    local world = {}
    
    world.config = config or Physics_defaultConfig
    world.bodies = {}
    world.staticBodies = {}
    world.dynamicBodies = {}
    world.kinematicBodies = {}
    world.collisionPairs = {}
    world.contacts = {}
    world.accumulator = 0
    world.broadphase = Physics_CreateBroadphase()
    
    return world
end

function Physics_AddBody(world, body)
    table.insert(world.bodies, body)
    
    if body.type == "static" then
        table.insert(world.staticBodies, body)
    elseif body.type == "dynamic" then
        table.insert(world.dynamicBodies, body)
    elseif body.type == "kinematic" then
        table.insert(world.kinematicBodies, body)
    end
    
    body.broadphaseId = Physics_BroadphaseAddBody(world.broadphase, body)
end

function Physics_RemoveBody(world, body)
    if body.broadphaseId then
        Physics_BroadphaseRemoveBody(world.broadphase, body.broadphaseId)
    end
    
    local function removeFromTable(tbl, item)
        for i = #tbl, 1, -1 do
            if tbl[i] == item then
                table.remove(tbl, i)
                return true
            end
        end
        return false
    end
    
    removeFromTable(world.bodies, body)
    removeFromTable(world.staticBodies, body)
    removeFromTable(world.dynamicBodies, body)
    removeFromTable(world.kinematicBodies, body)
end

function Physics_UpdateWorld(world, dt)
    world.accumulator = world.accumulator + dt
    
    while world.accumulator >= world.config.fixedTimestep do
        Physics_FixedUpdate(world, world.config.fixedTimestep)
        world.accumulator = world.accumulator - world.config.fixedTimestep
    end
end

function Physics_FixedUpdate(world, dt)
    Physics_BroadphaseUpdate(world.broadphase)
    
    world.collisionPairs = Physics_BroadphaseGetPotentialPairs(world.broadphase)
    world.contacts = {}
    
    Physics_DetectCollisions(world)
    Physics_ResolveCollisions(world, dt)
    Physics_Integrate(world, dt)
    
    world.contacts = {}
end

function Physics_DetectCollisions(world)
    for _, pair in ipairs(world.collisionPairs) do
        local bodyA, bodyB = pair.bodyA, pair.bodyB
        
        if bodyA.enabled and bodyB.enabled then
            if (bodyA.collisionLayer & bodyB.collisionMask) ~= 0 and
               (bodyB.collisionLayer & bodyA.collisionMask) ~= 0 then
            
                if Physics_CheckAABBOverlap(bodyA, bodyB) then
                    for _, colliderA in ipairs(bodyA.colliders) do
                        for _, colliderB in ipairs(bodyB.colliders) do
                            if colliderA.enabled and colliderB.enabled then
                                local collision = Collision_Check(bodyA, colliderA, bodyB, colliderB)
                                if collision then
                                    table.insert(world.contacts, collision)
                                end
                            end
                        end
                    end
                end
            end
        end
    end
end

function Physics_CheckAABBOverlap(bodyA, bodyB)
    local a = bodyA.aabb
    local b = bodyB.aabb
    
    return not (a.max[1] < b.min[1] or a.min[1] > b.max[1] or
                a.max[2] < b.min[2] or a.min[2] > b.max[2] or
                a.max[3] < b.min[3] or a.min[3] > b.max[3])
end

function Physics_ResolveCollisions(world, dt)
    for _, contact in ipairs(world.contacts) do
        local bodyA, bodyB = contact.bodyA, contact.bodyB
        
        if bodyA.isTrigger or bodyB.isTrigger or bodyA.isSensor or bodyB.isSensor then
            if bodyA.onTrigger then bodyA:onTrigger(bodyB, contact) end
            if bodyB.onTrigger then bodyB:onTrigger(bodyA, contact) end
        else
            Physics_ResolveContact(contact, dt)
            
            if bodyA.onCollision then bodyA:onCollision(bodyB, contact) end
            if bodyB.onCollision then bodyB:onCollision(bodyA, contact) end
        end
    end
end

function Physics_ResolveContact(contact, dt)
    local bodyA, bodyB = contact.bodyA, contact.bodyB
    
    if bodyA.invMass == 0 and bodyB.invMass == 0 then
        return
    end
    
    local relativeVelocity = vector3Sub(bodyB.velocity, bodyA.velocity)
    local velocityAlongNormal = vector3Dot(relativeVelocity, contact.normal)
    
    if velocityAlongNormal > 0 then
        return
    end
    
    local restitution = math.min(bodyA.restitution, bodyB.restitution)
    
    local impulseScalar = -(1 + restitution) * velocityAlongNormal
    impulseScalar = impulseScalar / (bodyA.invMass + bodyB.invMass)
    
    local impulse = vector3Mul(contact.normal, impulseScalar)
    
    bodyA.velocity[1] = bodyA.velocity[1] - impulse[1] * bodyA.invMass
    bodyA.velocity[2] = bodyA.velocity[2] - impulse[2] * bodyA.invMass
    bodyA.velocity[3] = bodyA.velocity[3] - impulse[3] * bodyA.invMass
    
    bodyB.velocity[1] = bodyB.velocity[1] + impulse[1] * bodyB.invMass
    bodyB.velocity[2] = bodyB.velocity[2] + impulse[2] * bodyB.invMass
    bodyB.velocity[3] = bodyB.velocity[3] + impulse[3] * bodyB.invMass
    
    local percent = 0.8
    local slop = 0.01
    local correction = math.max(contact.penetration - slop, 0) / (bodyA.invMass + bodyB.invMass) * percent
    
    local correctionVector = vector3Mul(contact.normal, correction)
    
    if bodyA.invMass > 0 then
        bodyA.position[1] = bodyA.position[1] - correctionVector[1] * bodyA.invMass
        bodyA.position[2] = bodyA.position[2] - correctionVector[2] * bodyA.invMass
        bodyA.position[3] = bodyA.position[3] - correctionVector[3] * bodyA.invMass
    end
    
    if bodyB.invMass > 0 then
        bodyB.position[1] = bodyB.position[1] + correctionVector[1] * bodyB.invMass
        bodyB.position[2] = bodyB.position[2] + correctionVector[2] * bodyB.invMass
        bodyB.position[3] = bodyB.position[3] + correctionVector[3] * bodyB.invMass
    end
end

function Physics_Integrate(world, dt)
    for _, body in ipairs(world.dynamicBodies) do
        Physics_ApplyForce(body, {
            world.config.gravity[1] * body.mass,
            world.config.gravity[2] * body.mass,
            world.config.gravity[3] * body.mass
        })
        Physics_UpdateBody(body, dt)
    end
    
    for _, body in ipairs(world.kinematicBodies) do
        Physics_UpdateBody(body, dt)
    end
end

-- Broadphase система
function Physics_CreateBroadphase()
    local bp = {}
    bp.bodies = {}
    bp.nextId = 1
    return bp
end

function Physics_BroadphaseAddBody(broadphase, body)
    local id = broadphase.nextId
    broadphase.bodies[id] = body
    broadphase.nextId = broadphase.nextId + 1
    return id
end

function Physics_BroadphaseRemoveBody(broadphase, id)
    broadphase.bodies[id] = nil
end

function Physics_BroadphaseUpdate(broadphase)
    -- В простой реализации ничего не делаем
end

function Physics_BroadphaseGetPotentialPairs(broadphase)
    local pairs = {}
    local bodiesArray = {}
    
    for id, body in pairs(broadphase.bodies) do
        if body.enabled then
            table.insert(bodiesArray, body)
        end
    end
    
    for i = 1, #bodiesArray do
        for j = i + 1, #bodiesArray do
            local bodyA, bodyB = bodiesArray[i], bodiesArray[j]
            
            local aabbA, aabbB = bodyA.aabb, bodyB.aabb
            if not (aabbA.max[1] < aabbB.min[1] or aabbA.min[1] > aabbB.max[1] or
                   aabbA.max[2] < aabbB.min[2] or aabbA.min[2] > aabbB.max[2] or
                   aabbA.max[3] < aabbB.min[3] or aabbA.min[3] > aabbB.max[3]) then
                table.insert(pairs, {bodyA = bodyA, bodyB = bodyB})
            end
        end
    end
    
    return pairs
end

-- Raycasting
function Physics_Raycast(world, origin, direction, maxDistance, layerMask)
    local normalizedDir = vector3Normalize(direction)
    local endPoint = vector3Add(origin, vector3Mul(normalizedDir, maxDistance))
    
    local closestHit = nil
    local closestDistance = math.huge
    
    for _, body in ipairs(world.bodies) do
        if body.enabled and (body.collisionLayer & (layerMask or 0xFFFF)) ~= 0 then
            local hit, distance = Physics_RaycastAABB(origin, endPoint, body.aabb)
            if hit and distance < closestDistance then
                closestHit = body
                closestDistance = distance
            end
        end
    end
    
    return closestHit, closestDistance
end

function Physics_RaycastAABB(origin, endPoint, aabb)
    local dir = vector3Sub(endPoint, origin)
    local length = vector3Length(dir)
    if length == 0 then return false, 0 end
    
    local invDir = {
        1 / (dir[1] ~= 0 and dir[1] or math.huge),
        1 / (dir[2] ~= 0 and dir[2] or math.huge),
        1 / (dir[3] ~= 0 and dir[3] or math.huge)
    }
    
    local t1 = (aabb.min[1] - origin[1]) * invDir[1]
    local t2 = (aabb.max[1] - origin[1]) * invDir[1]
    local t3 = (aabb.min[2] - origin[2]) * invDir[2]
    local t4 = (aabb.max[2] - origin[2]) * invDir[2]
    local t5 = (aabb.min[3] - origin[3]) * invDir[3]
    local t6 = (aabb.max[3] - origin[3]) * invDir[3]
    
    local tmin = math.max(math.max(math.min(t1, t2), math.min(t3, t4)), math.min(t5, t6))
    local tmax = math.min(math.min(math.max(t1, t2), math.max(t3, t4)), math.max(t5, t6))
    
    if tmax < 0 or tmin > tmax or tmin > length then
        return false, 0
    end
    
    return true, tmin
end

-- Фабричные функции
function Physics_CreateRigidBody(position, mass, colliders)
    return Physics_CreateBody({
        type = "dynamic",
        position = position,
        mass = mass,
        colliders = colliders or {}
    })
end

function Physics_CreateStaticBody(position, colliders)
    return Physics_CreateBody({
        type = "static",
        position = position,
        mass = 0,
        colliders = colliders or {}
    })
end

function Physics_CreateKinematicBody(position, colliders)
    return Physics_CreateBody({
        type = "kinematic",
        position = position,
        colliders = colliders or {}
    })
end

function Physics_CreateBoxCollider(size, offset)
    return Physics_CreateCollider("box", {
        size = size,
        offset = offset
    })
end

function Physics_CreateSphereCollider(radius, offset)
    return Physics_CreateCollider("sphere", {
        radius = radius,
        offset = offset
    })
end

function Physics_CreateCapsuleCollider(radius, height, offset)
    return Physics_CreateCollider("capsule", {
        radius = radius,
        height = height,
        offset = offset
    })
end

function Physics_CreatePlaneCollider(normal, distance, offset)
    return Physics_CreateCollider("plane", {
        normal = normal,
        distance = distance,
        offset = offset
    })
end

-- 2D фабрики
function Physics_Create2DRigidBody(x, y, mass, colliders)
    return Physics_CreateRigidBody({x, y, 0}, mass, colliders)
end

function Physics_Create2DStaticBody(x, y, colliders)
    return Physics_CreateStaticBody({x, y, 0}, colliders)
end

function Physics_Create2DBoxCollider(width, height, offset)
    return Physics_CreateBoxCollider({width, height, 0.1}, offset or {0, 0, 0})
end

function Physics_Create2DCircleCollider(radius, offset)
    return Physics_CreateSphereCollider(radius, offset or {0, 0, 0})
end

-- Обратная совместимость
function Physics_Create(player)
    local body = Physics_Create2DRigidBody(
        player.x or 0, player.y or 0, 
        player.mass or 1,
        {Physics_Create2DBoxCollider(player.w or 1, player.h or 1)}
    )
    
    if player.fallspeed then
        body.userData.fallspeed = player.fallspeed
    end
    if player.maxspeed then
        body.userData.maxspeed = player.maxspeed
    end
    
    body.userData.originalPlayer = player
    Physics_AddBody(Physics_defaultWorld, body)
    
    return body
end

-- Создание дефолтного мира
Physics_defaultWorld = Physics_CreateWorld()

return {
    -- Конфигурация
    defaultConfig = Physics_defaultConfig,
    layers = Physics_layers,
    collisionMatrix = Physics_collisionMatrix,
    
    -- Основные функции
    CreateWorld = Physics_CreateWorld,
    CreateBody = Physics_CreateBody,
    CreateCollider = Physics_CreateCollider,
    UpdateWorld = Physics_UpdateWorld,
    AddBody = Physics_AddBody,
    RemoveBody = Physics_RemoveBody,
    
    -- Фабрики тел
    CreateRigidBody = Physics_CreateRigidBody,
    CreateStaticBody = Physics_CreateStaticBody,
    CreateKinematicBody = Physics_CreateKinematicBody,
    
    -- Фабрики коллайдеров
    CreateBoxCollider = Physics_CreateBoxCollider,
    CreateSphereCollider = Physics_CreateSphereCollider,
    CreateCapsuleCollider = Physics_CreateCapsuleCollider,
    CreatePlaneCollider = Physics_CreatePlaneCollider,
    
    -- 2D фабрики
    Create2DRigidBody = Physics_Create2DRigidBody,
    Create2DStaticBody = Physics_Create2DStaticBody,
    Create2DBoxCollider = Physics_Create2DBoxCollider,
    Create2DCircleCollider = Physics_Create2DCircleCollider,
    
    -- Утилиты
    Raycast = Physics_Raycast,
    ApplyForce = Physics_ApplyForce,
    ApplyImpulse = Physics_ApplyImpulse,
    
    -- Обратная совместимость
    Create = Physics_Create,
    
    -- Дефолтный мир
    defaultWorld = Physics_defaultWorld
}